﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.IO;
using System.Text;
using System.Threading;
using System.Net.Sockets;

namespace EVDashboard
{
    //
    // Implements the ISystem (BMS) for Paktrakr
    //
    public class Paktrakr : ISystem
    {
        #region Private Declarations
        private string line;
        private List<List<float>> voltage;
        private List<int> reserved;
        private List<int> exceptions;

        private static int PACK_VOLTS = 0;
       // private static int PACK_SOC = 1;
        private static int PACK_TEMP = 2;
       // private static int PACK_SERIES = 3;
        private List<int> pack;

        private static int TIME_SECOND = 0;
        private static int TIME_MINUTE = 1;
        private static int TIME_HOUR = 2;
        private static int TIME_DAY = 3;
        private static int TIME_SOC = 4;
        private static int TIME_AMPS = 5;
        private List<int> time;

        private float voltageFactor = 100f; // data multiple either 10 or 100 
        private float amperageFactor = 10f;

        // Config data
        private BmsConfig bmsConfig = new BmsConfig();

        private int lastUptimeSeconds = 0;
        private double totalKiloWattHours = 0.0;

        private MainForm mainForm;
        #endregion

        public void Initialize(MainForm form)
        {
            mainForm = form;
        }

        public void SetBmsSetup(BmsSetup bmsSetup)
        {
            SetBatteryType(bmsSetup.BatteryType);
            SetVoltageFactor(bmsSetup.BatteryType);
        }

        public bool HasConfig()
        {
            return false;
        }

        public BmsData Parse(string line)
        {
            this.line = line;
            voltage = new List<List<float>>();
            pack = new List<int>();
            reserved = new List<int>();
            exceptions = new List<int>();
            time = new List<int>();

            if (line == null || line.Length <= 0 || line[0] != 'B')
            {
                throw new Exception("Invalid data " + line);
            }

            string[] all = line.Split(',');
            List<float> remote = null;

            string section = "";

            for (int i = 0; i < all.Length; i++)
            {
                string str = all[i];

                if (str.Equals("B"))
                {
                    section = str;
                    remote = new List<float>();
                    voltage.Add(remote);
                } else if (str.Equals("H")
                    || str.Equals("E")
                    || str.Equals("P")
                    || str.Equals("T"))
                {
                    section = str;
                } else {
                    int value = 0;
                    if (str.Equals("")
                        || str.Equals(" ")
                        || str.Equals("\r\n"))
                    {
                        value = 0;
                    }
                    else
                    {
                        try
                        {
                            value = Int32.Parse(str);
                        }
                        catch (Exception e)
                        {
                            throw new Exception(e.Message + ">" + (int)str[0] + "<");
                        }
                    }

                    if (section.Equals("B"))
                    {
                        float v = (float)value / voltageFactor;
                        remote.Add(v);
                    }
                    else if (section.Equals("E"))
                    {
                        exceptions.Add(value);
                    }
                    else if (section.Equals("H"))
                    {
                        reserved.Add(value);
                    }
                    else if (section.Equals("P"))
                    {
                        pack.Add(value);
                    }
                    else if (section.Equals("T"))
                    {
                        time.Add(value);
                    }
                    else
                        throw new Exception("Invalid section " + str);
                }

            }

            return GetBmsData();
        }

        private BmsData GetBmsData()
        {
            BmsData bmsData = new BmsData();
            bmsData.amps = GetAmps();
            bmsData.voltage = GetVoltage();
            bmsData.soc = GetSOC();
            bmsData.temp = GetTemp();
            bmsData.uptime = GetUptime();
            bmsData.voltages = GetVoltages();
            bmsData.line = line;
            bmsData.kwh = GetKwh();
            return bmsData;
        }

        public void Manager(Stream stream)
        {
            NetworkStream btStream = (NetworkStream)stream;
            StringBuilder sb = new StringBuilder();
            do
            {
                while (!btStream.DataAvailable)
                {
                    Thread.Sleep(100);
                    // TODO if more than 5 seconds out of range (sound)
                }

                Char ch = (Char)btStream.ReadByte();
                if (ch == '\n')
                {
                    sb.Append(ch);
                    string line = sb.ToString();

                    BmsData bmsData = Parse(line);
                    mainForm.Invoke(new MainForm.SetDataDeleg(mainForm.m_DataReceived), new object[] { bmsData });
 
                    sb = new StringBuilder();
                }
                else if (((int)ch) > -1)
                {
                    sb.Append(ch);
                }
            }
            while (true);
        }

        private List<List<float>> GetVoltages()
        {
            return voltage;
        }

        private int GetTemp()
        {
            // average temps?
            // convert to celcius
            int temp = (pack[PACK_TEMP] - 32) * 5 / 9;

            return temp;
        }

        // Sum the voltage from each remote
        private float GetVoltage()
        {
            int volts = 0;
            for (int i=0; i<voltage.Count; i++)
            {
                volts += pack[(4*i) + PACK_VOLTS];
            }

            return volts / voltageFactor;
        }

        private TimeSpan GetUptime()
        {
            return new TimeSpan(GetDay(), GetHour(), GetMinute(), GetSecond());
        }

        private int GetSecond()
        {
            return time[TIME_SECOND];
        }

        private int GetMinute()
        {
            return time[TIME_MINUTE];
        }

        private int GetHour()
        {
            return time[TIME_HOUR];
        }

        private int GetDay()
        {
            return time[TIME_DAY];
        }

        // Return the state of charger %
        private float GetSOC()
        {
            return (float)time[TIME_SOC];
        }

        private float GetAmps()
        {
            return (float)time[TIME_AMPS] / amperageFactor;
        }

        private float GetKwh()
        {
            // Find the watts
            int watts = (int)((GetAmps()) * (GetVoltage()));

            // Accumulate the totals
            int uptimeSeconds = (int)GetUptime().Seconds;
            int seconds = uptimeSeconds - lastUptimeSeconds;
            // First time through
            if (lastUptimeSeconds == 0)
                seconds = 1;
            lastUptimeSeconds = uptimeSeconds;

            // TODO move to Paktrakr
            totalKiloWattHours += watts * seconds / 3600.0 / 1000.0;

            return (float)totalKiloWattHours;
        }

        // Set the voltageFactor
        private void SetVoltageFactor(String batteryType)
        {
            if (batteryType.Equals("Lithium"))
            {
                voltageFactor = 100f;
            }
            else if (batteryType.Equals("Flooded(6V)"))
            {
                voltageFactor = 100f;
            }
            else if (batteryType.Equals("NiCd"))
            {
                voltageFactor = 100f;
            }
            else
            {
                voltageFactor = 10f;
            }
        }

        public string GetSimFileName()
        {
            return "paktrakr.csv";
        }

        public BmsConfig GetConfig()
        {
            return bmsConfig;
        }

        public void WriteConfig(BmsConfig bmsConfig)
        {
        }

        private void SetBatteryType(String batteryType)
        {
            //List v = new ArrayList() = { "AGM", 11.58, 13.08 };
            float low = 11;
            float high = 13;

            if (batteryType.Equals("AGM"))
            {
                low = 11.58f;
                high = 13.08f;
            }
            else if (batteryType.Equals("Lithium"))
            {
                low = 2.5f; // 3.1f;
                high = 4.0f; // 3.35f;
            }
            else if (batteryType.Equals("Flooded(6V)"))
            {
                low = 5.67f;
                high = 6.36f;
            }
            else if (batteryType.Equals("Flooded(8V)"))
            {
                low = 7.56f;
                high = 8.49f;
            }
            else if (batteryType.Equals("Flooded(12V)"))
            {
                low = 11.34f;
                high = 12.74f;
            }
            else if (batteryType.Equals("NiCd"))
            {
                low = 5.01f;
                high = 6.81f;
            }

            bmsConfig.OverVoltage = high;
            bmsConfig.UnderVoltage = low;
        }


    }
}
